﻿using System;
using System.Collections.Generic;
using System.Linq;
using Internal.Runtime.Services.BattleRecord;
using KuiHuaBaoDian.Services;
using Runtime.Services.Battle.Entity;
using static Runtime.Services.BattleRecord.IDFR_BattleRecordService;

namespace Runtime.Services.BattleRecord {

    /// <summary>
    /// 战斗记录服务
    /// </summary>
    public interface IDFR_BattleRecordService {

        /// <summary>
        /// 伤害
        /// </summary>
        public readonly struct Damage : IComparable<Damage> {

            /// <summary>
            /// 值
            /// </summary>
            public int Value { get; init; }

            public override string ToString() => Value.ToString();

            int IComparable<Damage>.CompareTo(Damage other) {
                if (Value > other.Value) {
                    return 1;
                } else if (Value == other.Value) {
                    return 0;
                } else {
                    return -1;
                }
            }
        }

        /// <summary>
        /// 击杀数量
        /// </summary>
        public readonly struct KillCount : IComparable<KillCount> {

            /// <summary>
            /// 值
            /// </summary>
            public int Value { get; init; }

            public static KillCount operator ++(KillCount a) => new() { Value = a.Value + 1 };

            public override string ToString() => Value.ToString();

            int IComparable<KillCount>.CompareTo(KillCount other) {
                if (Value > other.Value) {
                    return 1;
                } else if (Value == other.Value) {
                    return 0;
                } else {
                    return -1;
                }
            }
        }

        /// <summary>
        /// 操作数量
        /// </summary>
        public readonly struct OperationCount {

            /// <summary>
            /// 值
            /// </summary>
            public int Value { get; init; }

            public static OperationCount operator ++(OperationCount a) => new() { Value = a.Value + 1 };

            public override string ToString() => Value.ToString();
        }

        /// <summary>
        /// 击杀类型
        /// </summary>
        public readonly struct KillType {

            public static readonly KillType Invalid = new KillType();

            /// <summary>
            /// 值
            /// </summary>
            public int Value { get; init; }

            public override bool Equals(object obj) {
                if (obj is KillType other) {
                    return other.Value == Value;
                }
                return false;
            }

            public override int GetHashCode() => Value.GetHashCode();
        }

        /// <summary>
        /// 操作类型
        /// </summary>
        public readonly struct OperationType {

            /// <summary>
            /// 值
            /// </summary>
            public int Value { get; init; }

            public override bool Equals(object obj) {
                if (obj is OperationType other) {
                    return other.Value == Value;
                }
                return false;
            }

            public override int GetHashCode() => Value.GetHashCode();
        }

        /// <summary>
        /// 总伤害
        /// </summary>
        Damage SumDamage();

        /// <summary>
        /// 总击杀
        /// </summary>
        KillCount SumKillCount();

        /// <summary>
        /// 伤害列表
        /// </summary>
        IEnumerable<KeyValuePair<DFR_BattleEntityID, Damage>> GetSumDamageDictionary();

        /// <summary>
        /// 伤害列表
        /// </summary>
        IEnumerable<(DFR_BattleEntityID entityID, long damageRaw)> GetSumDamageRaws();

        /// <summary>
        /// 击杀列表
        /// </summary>
        IReadOnlyDictionary<KillType, KillCount> GetSumKillCountDictionary();

        /// <summary>
        /// 操作计数列表
        /// </summary>
        IReadOnlyDictionary<OperationType, OperationCount> GetOperationCountDictionary();

        /// <summary>
        /// 获取操作计数
        /// </summary>
        int GetOperationCount(OperationType operationType);

        /// <summary>
        /// 攻击者实例的最后一次普通攻击的记录
        /// </summary>
        /// <param name="instanceID">魔物实例ID</param>
        IDFR_BattleRecordAttack GetAttackSourceInstanceLastNormalAttack(DFR_BattleEntityInstanceID instanceID);

        /// <summary>
        /// 被击杀的目标列表
        /// </summary>
        IReadOnlyDictionary<DFR_BattleEntityID, KillCount> GetTargetBeKilledCountDictionary();
    }

    public interface IDFR_BattleRecordServiceWritter {

        /// <summary>
        /// 提交
        /// </summary>
        /// <param name="record">攻击纪录</param>
        void Submit(IDFR_BattleRecordAttack record);

        /// <summary>
        /// 提交
        /// </summary>
        /// <param name="operationType">操作类型</param>
        void Submit(OperationType operationType);
    }

    /// <summary>
    /// 战斗记录服务
    /// </summary>
    public static class BattleRecordService {

        /// <summary>
        /// 单例
        /// </summary>
        public static IDFR_BattleRecordService Instance => m_Instance ??= KHBD.TypeMap.Instantiate<IDFR_BattleRecordService>();
        private static IDFR_BattleRecordService m_Instance;

        /// <summary>
        /// 写入
        /// </summary>
        public static IDFR_BattleRecordServiceWritter Writter => m_Writter ??= Instance as IDFR_BattleRecordServiceWritter;
        private static IDFR_BattleRecordServiceWritter m_Writter;
    }

    [Implement(typeof(IDFR_BattleRecordService))]
    internal sealed class DFR_BattleRecordService : IDFR_BattleRecordService, IDFR_BattleRecordServiceInternal, IDFR_BattleRecordServiceWritter, IService {

        private readonly struct DamageRaw {
            public long Value { get; init; }
            public static DamageRaw operator +(DamageRaw left, long value) => new() { Value = left.Value + value };
        }

        private readonly Queue<IDFR_BattleRecordAttack> m_RecordQueue = new();

        void IService.OnApplicationQuit() {
            m_RecordQueue.Clear();
            m_KillType_KillCount_Dictionary.Clear();
            m_AttackSourceInstance_NormalAttackRecordLinkedList_Dictionary.Clear();
            m_OperationType_OperationCount_Dictionary.Clear();
            m_Source_DamageRaw_Dictionary.Clear();
            m_Target_BeKilledCount_Dictionary.Clear();
            m_RecordLinkedListStack.Clear();
        }

        void IDFR_BattleRecordServiceInternal.ClearRecordQueue() {
            foreach (var record in m_RecordQueue) {
                record.Dispose();
            }
            m_RecordQueue.Clear();

            m_TotalDamageRaw = 0;
            m_TotalKillCount = 0;
            m_KillType_KillCount_Dictionary.Clear();
            m_OperationType_OperationCount_Dictionary.Clear();
            foreach (var kv in m_AttackSourceInstance_NormalAttackRecordLinkedList_Dictionary) {
                var linkedList = kv.Value;
                linkedList.Clear();
                m_RecordLinkedListStack.Push(linkedList);
            }
            m_AttackSourceInstance_NormalAttackRecordLinkedList_Dictionary.Clear();
            m_Source_DamageRaw_Dictionary.Clear();
            m_Target_BeKilledCount_Dictionary.Clear();
        }

        private long m_TotalDamageRaw;
        private int m_TotalKillCount;

        private readonly Dictionary<DFR_BattleEntityID, DamageRaw> m_Source_DamageRaw_Dictionary = new();
        private readonly Dictionary<KillType, KillCount> m_KillType_KillCount_Dictionary = new();
        private readonly Dictionary<OperationType, OperationCount> m_OperationType_OperationCount_Dictionary = new();
        private readonly Dictionary<DFR_BattleEntityID, KillCount> m_Target_BeKilledCount_Dictionary = new();

        private readonly Dictionary<DFR_BattleEntityInstanceID, LinkedList<IDFR_BattleRecordAttack>> m_AttackSourceInstance_NormalAttackRecordLinkedList_Dictionary = new();
        private readonly Stack<LinkedList<IDFR_BattleRecordAttack>> m_RecordLinkedListStack = new();

        void IDFR_BattleRecordServiceInternal.Initialize(IEnumerable<KillType> killTypeList, IEnumerable<OperationType> operationTypeList) {
            foreach (var killType in killTypeList) {
                m_KillType_KillCount_Dictionary[killType] = new();
            }
            foreach (var operationType in operationTypeList) {
                m_OperationType_OperationCount_Dictionary[operationType] = new();
            }
        }

        void IDFR_BattleRecordServiceWritter.Submit(IDFR_BattleRecordAttack record) {
            m_RecordQueue.Enqueue(record);

            var sourceEntityInstanceID = record.Snapshot.SourceEntityInstanceID;
            var sourceEntityID = sourceEntityInstanceID.ID;
            if (!m_Source_DamageRaw_Dictionary.TryGetValue(sourceEntityID, out var damageRaw)) {
                damageRaw = m_Source_DamageRaw_Dictionary[sourceEntityID] = new DamageRaw();
            }
            damageRaw += record.Damage.Value.Raw;
            m_Source_DamageRaw_Dictionary[sourceEntityID] = damageRaw;
            m_TotalDamageRaw += record.Damage.Value.Raw;
            var hitTarget = record.HitTarget;
            if (hitTarget.IsKill) {
                m_TotalKillCount++;
                if (m_KillType_KillCount_Dictionary.ContainsKey(hitTarget.KillType)) {
                    m_KillType_KillCount_Dictionary[hitTarget.KillType]++;
                }
                var targetEntityID = record.HitTarget.InstanceID.ID;
                if (!m_Target_BeKilledCount_Dictionary.TryGetValue(targetEntityID, out var beKilledCount)) {
                    beKilledCount = m_Target_BeKilledCount_Dictionary[targetEntityID] = new KillCount { Value = 0 };
                }
                m_Target_BeKilledCount_Dictionary[targetEntityID] = ++beKilledCount;
            }
            if (!m_AttackSourceInstance_NormalAttackRecordLinkedList_Dictionary.TryGetValue(sourceEntityInstanceID, out var normalAttackRecordLinkedList)) {
                if (!m_RecordLinkedListStack.TryPop(out normalAttackRecordLinkedList)) {
                    normalAttackRecordLinkedList = m_AttackSourceInstance_NormalAttackRecordLinkedList_Dictionary[sourceEntityInstanceID] = new LinkedList<IDFR_BattleRecordAttack>();
                }
            }
            normalAttackRecordLinkedList.AddFirst(record);
        }

        void IDFR_BattleRecordServiceWritter.Submit(OperationType operationType) {
            m_OperationType_OperationCount_Dictionary[operationType]++;
        }

        public IEnumerable<KeyValuePair<DFR_BattleEntityID, Damage>> GetSumDamageDictionary() {
            foreach (var kv in m_Source_DamageRaw_Dictionary) {
                yield return new(kv.Key, new Damage { Value = F64.FromRaw(kv.Value.Value).Int });
            }
        }

        public IEnumerable<(DFR_BattleEntityID entityID, long damageRaw)> GetSumDamageRaws() => m_Source_DamageRaw_Dictionary.Select(kv => (kv.Key, kv.Value.Value));

        public IReadOnlyDictionary<KillType, KillCount> GetSumKillCountDictionary() => m_KillType_KillCount_Dictionary;

        public IReadOnlyDictionary<OperationType, OperationCount> GetOperationCountDictionary() => m_OperationType_OperationCount_Dictionary;

        public int GetOperationCount(OperationType operationType) => m_OperationType_OperationCount_Dictionary[operationType].Value;

        public IReadOnlyDictionary<DFR_BattleEntityID, KillCount> GetTargetBeKilledCountDictionary() => m_Target_BeKilledCount_Dictionary;

        public Damage SumDamage() {
            return new Damage { Value = F64.FromRaw(m_TotalDamageRaw).Int };
        }

        public KillCount SumKillCount() {
            return new KillCount { Value = m_TotalKillCount };
        }

        public IDFR_BattleRecordAttack GetAttackSourceInstanceLastNormalAttack(DFR_BattleEntityInstanceID instanceID) {
            if (m_AttackSourceInstance_NormalAttackRecordLinkedList_Dictionary.TryGetValue(instanceID, out var normalAttackRecordLinkedList)) {
                return normalAttackRecordLinkedList.First.Value;
            }
            return null;
        }

        void IDFR_BattleRecordServiceInternal.Recover(
            IReadOnlyDictionary<DFR_BattleEntityID, long> sumDamageRawDictionary,
            IReadOnlyDictionary<KillType, KillCount> sumKillCountDictionary,
            IReadOnlyDictionary<OperationType, OperationCount> operationCountDictionary,
            IReadOnlyDictionary<DFR_BattleEntityID, KillCount> targetBeKilledCountDictionary) {

            if (sumDamageRawDictionary != null) {
                m_Source_DamageRaw_Dictionary.Clear();
                m_TotalDamageRaw = default;
                foreach (var kv in sumDamageRawDictionary) {
                    m_Source_DamageRaw_Dictionary.Add(kv.Key, new DamageRaw { Value = kv.Value });
                    m_TotalDamageRaw += kv.Value;
                }
            }

            if (sumKillCountDictionary != null) {
                m_KillType_KillCount_Dictionary.Clear();
                m_TotalKillCount = default;
                foreach (var kv in sumKillCountDictionary) {
                    m_KillType_KillCount_Dictionary.Add(kv.Key, kv.Value);
                    m_TotalKillCount += kv.Value.Value;
                }
            }

            if (operationCountDictionary != null) {
                m_OperationType_OperationCount_Dictionary.Clear();
                foreach (var kv in operationCountDictionary) {
                    m_OperationType_OperationCount_Dictionary.Add(kv.Key, kv.Value);
                }
            }

            if (targetBeKilledCountDictionary != null) {
                m_Target_BeKilledCount_Dictionary.Clear();
                foreach (var kv in targetBeKilledCountDictionary) {
                    m_Target_BeKilledCount_Dictionary.Add(kv.Key, kv.Value);
                }
            }
        }
    }
}

namespace Internal.Runtime.Services.BattleRecord {

    public interface IDFR_BattleRecordServiceInternal {

        void Initialize(IEnumerable<KillType> killTypeList, IEnumerable<OperationType> operationList);

        void ClearRecordQueue();

        void Recover(
            IReadOnlyDictionary<DFR_BattleEntityID, long> sumDamageRawDictionary,
            IReadOnlyDictionary<KillType, KillCount> sumKillCountDictionary,
            IReadOnlyDictionary<OperationType, OperationCount> operationCountDictionary,
            IReadOnlyDictionary<DFR_BattleEntityID, KillCount> targetBeKilledCountDictionary);
    }
}
